#include <dspSimulator/instruction/callp.h>

bool CALLP::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    return true;
}

void CALLP::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                              std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> CALLP::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<CALLP> callp;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<4>(data, 28, 0b0001) && checkBits<5>(data, 2, 0b00100)) {
            callp = std::make_shared<CALLP>(dspInstInfo->getAddress(), baseCycle);
            callp->cst = extractBits<21>(data, 7).to_ulong();
            callp->s = data[1];
            callp->crhi = 0b00000;         // point to PCE1
            callp->crlo = 0b10000;
            callp->dst = 0b11;
            callp->p = data[0];
            callp->numOfInsertNOP = 5;
            if (callp->s == 0) {
                callp->funcUnit = FuncUnit::S1;
            } else {
                callp->funcUnit = FuncUnit::S2;
            }
            callp->instFormat = InstFormat::Kind1;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (checkBits<5>(data, 1, 0b01101)) {
            callp = std::make_shared<CALLP>(dspInstInfo->getAddress(), baseCycle);
            callp->isCompactInstruction = true;
            callp->compactInstKind = "Scs10";
            callp->cst = extractBitsAndToInt<10>(extractBits<10>(data, 6));
            callp->s = data[0];
            callp->crhi = 0b00000;         // point to PCE1
            callp->crlo = 0b10000;
            callp->dst = 0b11;
            callp->numOfInsertNOP = 5;
            if (callp->s == 0) {
                callp->funcUnit = FuncUnit::S1;
            } else {
                callp->funcUnit = FuncUnit::S2;
            }
            callp->PROT = dspInstInfo->getPROT();
            callp->RS = dspInstInfo->getRS();
            callp->BR = dspInstInfo->getBR();
            callp->DSZ = dspInstInfo->getDSZ();
            callp->SAT = dspInstInfo->getSAT();
            callp->p = dspInstInfo->getP();
            callp->fixUpRegOnCompactInstruction();
            callp->instFormat = InstFormat::Kind1;
        }
    }
    return callp;
}

void CALLP::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    if (executePhase == ExecutePhase::E6) {
        auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
        auto pce1 = *cRegPtr;
        auto scst21 = extractBitsAndToInt<21>(cst);
        auto pcReg = dspMachine->getRegisters()->getReg32ByName("pc");
        auto pc = *pcReg->getDataPtr();
        pcReg->updateData(pce1 + (scst21 << 2));
        *dstLoRegPtr = pc;
    }
}

std::string CALLP::toString() const {
    return m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " + dec2hex(extractBitsAndToInt<21>(cst)) + "," +
           getDstReg32Name();
}

std::shared_ptr<DspInstruction> CALLP::clone() {
    return std::make_shared<CALLP>(*this);
}
